﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Physic = StillDesign.PhysX; //PhysX
using PhysicMath = StillDesign.PhysX.MathPrimitives; //PhysX
namespace Game
{
    /*-------------------------------------------------------------------------
       | PhysicCharacter:
       | ------------
       | Physic simulation type "character" for body
       |---------------------------------------------------------------------------*/
    public class PhysicCharacter : Body
    {
        public class Hit : Physic.UserControllerHitReport
        {
            public override Physic.ControllerAction OnControllerHit(Physic.ControllersHit hit)
            {
                return Physic.ControllerAction.None;
            }

            public override Physic.ControllerAction OnShapeHit(Physic.ControllerShapeHit hit)
            {
                
                if (hit.Shape != null)
                {
                    short group = hit.Shape.Group;
                    Physic.Actor actor = hit.Shape.Actor;

                    //if (group == 1)
                    {
                        if (actor.IsDynamic && !actor.BodyFlags.Kinematic)
                        {
                            // We only allow horizontal pushes. Vertical pushes when we stand on dynamic objects creates
                            // useless stress on the solver. It would be possible to enable/disable vertical pushes on
                            // particular objects, if the gameplay requires it.
                            //Log.Hint(String.Format("hit.MotionDirection {3} x {0} y {1} z {2}", hit.MotionDirection.X, hit.MotionDirection.Y, hit.MotionDirection.Z, actor.Name));

                            if (hit.MotionDirection.Y == 0.0f)
                            {
                                float coeff = actor.Mass * hit.MotionLength * 10.0f;

                                actor.AddForceAtLocalPosition(hit.MotionDirection * coeff, new PhysicMath.Vector3(0, 0, 0), Physic.ForceMode.Impulse, true);
                            }
                        }
                    }
                }

                return Physic.ControllerAction.None;
            }
        }

        private Physic.Controller mController;
        private PhysicMath.Vector3 mAcceleration;
        private IDK.Quaternion mFromOrientation = IDK.Quaternion.Identity;
        private IDK.Quaternion mToOrientation = IDK.Quaternion.Identity;
        private uint mCollision;

        private bool mOnGround = false;

        public PhysicCharacter(Physic.CapsuleController controller, IDK.SceneNode node)
            : base(node)
        {
            mController = controller;
            IsKinematic = true;
            mCollision = uint.MaxValue;
        }

        public override void Collision(bool enable)
        {
            mCollision = (enable) ? uint.MaxValue : 0; 
        }


        public override void Reset()
        {
            mAcceleration = PhysicMath.Vector3.Zero;
        }

        public override void Rotate(IDK.Quaternion q)
        {
            mToOrientation = IDK.Quaternion.Multiply(mFromOrientation, q);
        }

        public override void SetOrientation(IDK.Quaternion q)
        {
            mToOrientation = q;
        }

        public override void LookAtPoint(IDK.Vector3 point)
        {
            IDK.Vector3 pos = GetPosition();
            IDK.Vector3 direction = point - pos;
            direction.y = 0; //lock y axis

            mToOrientation = IDK.Quaternion.RotationTo(IDK.Vector3.UpZ, direction);
        }

        public override void SetPosition(IDK.Vector3 pos)
        {
            mController.Position = IDKToPhysic.ToVector3(pos);
            //base.SetPosition(pos);
        }

        public override IDK.Vector3 GetPosition()
        {
            return IDKToPhysic.ToVector3(mController.Position);
        }

        public override void OnUpdate(Events.ProcessSceneUpdate evnt)
        {
            IDK.Vector3 position = IDKToPhysic.ToVector3(mController.Position) - PivotPosition;

            
            // apply gravity and jumping
            float mass = 3;

            if (mOnGround)
            {
                mass = 0.1f;
            }

            var g = new PhysicMath.Vector3(0.0f, -9.81f * mass, 0.0f); //TODO get this from physx 
            mAcceleration += (g * evnt.ElapsedTime);

            //IDK.Vector3 movement = (IDK.Quaternion.Multiply(IDK.Quaternion.Inverse(mFromOrientation), IDKToPhysic.ToVector3(mAcceleration))) * evnt.ElapsedTime;
            IDK.Vector3 movement = IDKToPhysic.ToVector3(mAcceleration) * evnt.ElapsedTime;
            // move the controller
            Physic.ControllerMoveResult result = mController.Move(IDKToPhysic.ToVector3(movement), mCollision, 0.0001f, 1);

            mOnGround = (result.CollisionFlag & Physic.ControllerCollisionFlag.Down) == Physic.ControllerCollisionFlag.Down;

            if (mOnGround)
            {
                mAcceleration.Y = 0;
            }

            //
            mAcceleration.X = 0;
            mAcceleration.Z = 0;

            //
            //Rotating 
            //
            //TODO: HARDCODE FOR JUST A TEST
            float RotationFactor = 10;

            mFromOrientation = IDK.Quaternion.Lerp(IDK.Quaternion.InterpolationMode.LinearSpherical, mFromOrientation, mToOrientation, evnt.ElapsedTime * RotationFactor);
            SceneNode.GetTransformation().SetTransform(mFromOrientation, position);
        }

        bool InJump
        {
            get { return !mOnGround; }
        }
        bool OnGround
        {
            get { return mOnGround; }
        }

        public override void AddWorldImpulse(IDK.Vector3 pos)
        {
            if (OnGround)
            {
                mAcceleration.Y = pos.y;
            }
        }

        public override void AddWorldForce(IDK.Vector3 pos)
        {
            PhysicMath.Vector3 temp = IDKToPhysic.ToVector3(pos);
            mAcceleration.X = temp.X;
            //mAcceleration.Y = temp.Y;
            mAcceleration.Z = temp.Z;
        }
    }
}
